/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.server.servlet.book;

import offset.nodes.client.dialog.io.model.BookType;
import offset.nodes.server.servlet.book.provider.NodeProvider;
import offset.nodes.server.servlet.book.provider.PageProvider;
import offset.nodes.server.servlet.book.content.Page;
import offset.nodes.server.servlet.book.provider.PageIterator;
import offset.nodes.server.servlet.book.content.Resource;
import java.io.IOException;
import java.util.Iterator;
import javax.servlet.ServletException;
import offset.nodes.Constants;
import offset.nodes.server.servlet.book.content.BookPageType;
import offset.nodes.server.servlet.book.content.Content;
import offset.nodes.server.servlet.book.content.CssResource;
import offset.nodes.server.servlet.book.content.TitlePage;
import offset.nodes.server.servlet.book.provider.ResourceInfo;

/**
 *
 * @author Walter L�tgenau
 */
public class BookCreator {

    PageProvider pageProvider;
    NodeProvider nodeProvider;

    public static final String PATH_TITLE_PAGE = "/jcr:system/nodes/resources/titlepage.html";

    /**
     * Constructor of the book creator
     * 
     * @param pageProvider a helper, that provides the pages
     */
    public BookCreator(PageProvider pageProvider, NodeProvider nodeProvider) {
        this.pageProvider = pageProvider;
        this.nodeProvider = nodeProvider;
    }

    /**
     * Create a book of the specified type. The root page is located at path,
     * up to maxRecursionLevel referenced pages will be included in the book.
     *
     * @param type the book type
     * @param path the path to the root page
     * @param maxRecursionLevel up to this level will references be followed
     * @return a book of the specified type
     * @throws ServletException
     * @throws IOException
     */
    public Book createBook(BookType type, String path, int maxRecursionLevel, boolean followLinks) throws ServletException, IOException {
        try {
            Book book = BookFactory.createBook(type);
            pageProvider.setBookType(type);
            book.setLocale(pageProvider.getRequest().getLocale());

            Page root = new Page(book, 0, path, pageProvider.getHtmlByPath(path), nodeProvider, followLinks, BookPageType.inTree);
            BookInfo bookInfo = nodeProvider.getBookInfo(root.getRepositoryRef());
            bookInfo.setId(root.getRepositoryRef());
            bookInfo.setLocale(pageProvider.getRequest().getLocale());
            book.setInfo(bookInfo);

            PageIterator pageIterator = new PageIterator(book, root, pageProvider, nodeProvider, maxRecursionLevel, followLinks);

            ResourceInfo titleContent = pageProvider.getResourceByPath(PATH_TITLE_PAGE);
            Page title = new TitlePage(book, -1, PATH_TITLE_PAGE, new String(titleContent.getContent()), nodeProvider, bookInfo, followLinks);
            addPage(book, title);

            addPage(book, root);
            while (pageIterator.hasNextPage()) {
                Page page = pageIterator.nextPage();
                addPage(book, page);
            }

            return book;
        } catch (Exception ex) {
            throw new IOException(ex);
        }
    }

    /**
     * Add a page to the book. This includes adding the resources.
     * 
     * @param book
     * @param page
     * @throws IOException
     */
    protected void addPage(Book book, Page page) throws IOException, ServletException {
        boolean alreadyInBook = book.containsPage(page.getRepositoryRef(), page.getBookRef());
        
        // give the book a chance to update its table of contents in any case
        book.addPage(page.getRepositoryRef(), page.getBookRef(), page.getName(), page.getContent().getString(), page.getLevel());
        if (alreadyInBook)
            return; // retrieve resources only once
        
        Iterator<Resource> resourceIterator = page.getResources().iterator();
        while (resourceIterator.hasNext()) {
            Resource resource = resourceIterator.next();

            // retrieve resource content
            ResourceInfo resourceInfo = pageProvider.getResourceByUrl(resource.getRepositoryRef());

            if (resourceInfo.getContentType().equals(Constants.MIMETYPE_CSS)) {
                CssResource css = new CssResource(resource.getRepositoryRef(), new String(resourceInfo.getContent()), book.getNameBuilder());
                
                resourceInfo.setContent(css.getContent().getBytes());
                addResource(book, resource, resourceInfo);

                Iterator<Resource> imageIterator = css.getResources().iterator();
                while(imageIterator.hasNext()) {
                    Resource image = imageIterator.next();

                    ResourceInfo imageInfo = pageProvider.getResourceByUrl(image.getRepositoryRef());
                    addResource(book, image, imageInfo);
                }
            } else
                addResource(book, resource, resourceInfo);
        }
    }

    /**
     * Add a resource to the book
     * 
     * @param book
     * @param resource
     * @throws IOException
     * @throws ServletException
     */
    protected void addResource(Book book, Resource resource, ResourceInfo resourceInfo) throws IOException, ServletException {
            // retrieve resource content
            Content content = resource.getContent();
            if (content == null || resourceInfo.getContent() == null)
                return;
            
            content.setBinary(resourceInfo.getContent());
            content.setContentType(resourceInfo.getContentType());

            if (resource.getContent().isBinary())
                book.addResource(resource.getRepositoryRef(), resource.getBookRef(), resource.getContent().getContentType(), resource.getContent().getBinary());
            else
                book.addResource(resource.getRepositoryRef(), resource.getBookRef(), resource.getContent().getContentType(), resource.getContent().getString());
    }
}
